#include "sqpcheader.h"
#include <stdarg.h>
#include <setjmp.h>
#include "sqopcodes.h"
#include "sqstring.h"
#include "sqfuncproto.h"
#include "sqcompiler.h"
#include "sqfuncstate.h"
#include "sqlexer.h"
#include "sqvm.h"
#include "sqtable.h"

#define DEREF_NO_DEREF  -1
#define DEREF_FIELD   -2

struct ExpState {
  ExpState() {
    _deref = DEREF_NO_DEREF;
    _freevar = false;
    _class_or_delete = false;
    _funcarg = false;
  }
  bool _class_or_delete;
  bool _funcarg;
  bool _freevar;
  SQInteger _deref;
};

typedef sqvector<ExpState> ExpStateVec;

#define _exst (_expstates.top())

#define BEGIN_BREAKBLE_BLOCK()  SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \
  SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \
  _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0);

#define END_BREAKBLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \
    __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \
    if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \
    if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \
    _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back();}

class SQCompiler {
  public:
    SQCompiler( SQVM *v, SQLEXREADFUNC rg, SQUserPointer up, const SQChar* sourcename, bool raiseerror, bool lineinfo ) {
      _vm = v;
      _lex.Init( _ss( v ), rg, up, ThrowError, this );
      _sourcename = SQString::Create( _ss( v ), sourcename );
      _lineinfo = lineinfo;
      _raiseerror = raiseerror;
      compilererror = NULL;
    }
    static void ThrowError( void *ud, const SQChar *s ) {
      SQCompiler *c = ( SQCompiler * )ud;
      c->Error( s );
    }
    void Error( const SQChar *s, ... ) {
      static SQChar temp[256];
      va_list vl;
      va_start( vl, s );
      scvsprintf( temp, s, vl );
      va_end( vl );
      compilererror = temp;
      longjmp( _errorjmp, 1 );
    }
    void Lex() { _token = _lex.Lex();}
    void PushExpState() { _expstates.push_back( ExpState() ); }
    bool IsDerefToken( SQInteger tok ) {
      switch( tok ) {
        case _SC( '=' ):
        case _SC( '(' ):
        case TK_NEWSLOT:
        case TK_MODEQ:
        case TK_MULEQ:
        case TK_DIVEQ:
        case TK_MINUSEQ:
        case TK_PLUSEQ:
        case TK_PLUSPLUS:
        case TK_MINUSMINUS:
          return true;
      }
      return false;
    }
    ExpState PopExpState() {
      ExpState ret = _expstates.top();
      _expstates.pop_back();
      return ret;
    }
    SQObject Expect( SQInteger tok ) {
      if( _token != tok ) {
        if( _token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER ) {
        } else {
          const SQChar *etypename;
          if( tok > 255 ) {
            switch( tok ) {
              case TK_IDENTIFIER:
                etypename = _SC( "IDENTIFIER" );
                break;
              case TK_STRING_LITERAL:
                etypename = _SC( "STRING_LITERAL" );
                break;
              case TK_INTEGER:
                etypename = _SC( "INTEGER" );
                break;
              case TK_FLOAT:
                etypename = _SC( "FLOAT" );
                break;
              default:
                etypename = _lex.Tok2Str( tok );
            }
            Error( _SC( "expected '%s'" ), etypename );
          }
          Error( _SC( "expected '%c'" ), tok );
        }
      }
      SQObjectPtr ret;
      switch( tok ) {
        case TK_IDENTIFIER:
          ret = _fs->CreateString( _lex._svalue );
          break;
        case TK_STRING_LITERAL:
          ret = _fs->CreateString( _lex._svalue, _lex._longstr.size() - 1 );
          break;
        case TK_INTEGER:
          ret = SQObjectPtr( _lex._nvalue );
          break;
        case TK_FLOAT:
          ret = SQObjectPtr( _lex._fvalue );
          break;
      }
      Lex();
      return ret;
    }
    bool IsEndOfStatement() { return ( ( _lex._prevtoken == _SC( '\n' ) ) || ( _token == SQUIRREL_EOB ) || ( _token == _SC( '}' ) ) || ( _token == _SC( ';' ) ) ); }
    void OptionalSemicolon() {
      if( _token == _SC( ';' ) ) {
        Lex();
        return;
      }
      if( !IsEndOfStatement() ) {
        Error( _SC( "end of statement expected (; or lf)" ) );
      }
    }
    void MoveIfCurrentTargetIsLocal() {
      SQInteger trg = _fs->TopTarget();
      if( _fs->IsLocal( trg ) ) {
        trg = _fs->PopTarget();
        _fs->AddInstruction( _OP_MOVE, _fs->PushTarget(), trg );
      }
    }
    bool Compile( SQObjectPtr &o ) {
      _debugline = 1;
      _debugop = 0;
      SQFuncState funcstate( _ss( _vm ), NULL, ThrowError, this );
      funcstate._name = SQString::Create( _ss( _vm ), _SC( "main" ) );
      _fs = &funcstate;
      _fs->AddParameter( _fs->CreateString( _SC( "this" ) ) );
      _fs->_sourcename = _sourcename;
      SQInteger stacksize = _fs->GetStackSize();
      if( setjmp( _errorjmp ) == 0 ) {
        Lex();
        while( _token > 0 ) {
          Statement();
          if( _lex._prevtoken != _SC( '}' ) ) {
            OptionalSemicolon();
          }
        }
        CleanStack( stacksize );
        _fs->AddLineInfos( _lex._currentline, _lineinfo, true );
        _fs->AddInstruction( _OP_RETURN, 0xFF );
        _fs->SetStackSize( 0 );
        o = _fs->BuildProto();
        #ifdef _DEBUG_DUMP
        _fs->Dump( _funcproto( o ) );
        #endif
      } else {
        if( _raiseerror && _ss( _vm )->_compilererrorhandler ) {
          _ss( _vm )->_compilererrorhandler( _vm, compilererror, type( _sourcename ) == OT_STRING ? _stringval( _sourcename ) : _SC( "unknown" ),
                                             _lex._currentline, _lex._currentcolumn );
        }
        _vm->_lasterror = SQString::Create( _ss( _vm ), compilererror, -1 );
        return false;
      }
      return true;
    }
    void Statements() {
      while( _token != _SC( '}' ) && _token != TK_DEFAULT && _token != TK_CASE ) {
        Statement();
        if( _lex._prevtoken != _SC( '}' ) && _lex._prevtoken != _SC( ';' ) ) {
          OptionalSemicolon();
        }
      }
    }
    void Statement() {
      _fs->AddLineInfos( _lex._currentline, _lineinfo );
      switch( _token ) {
        case _SC( ';' ):
          Lex();
          break;
        case TK_IF:
          IfStatement();
          break;
        case TK_WHILE:
          WhileStatement();
          break;
        case TK_DO:
          DoWhileStatement();
          break;
        case TK_FOR:
          ForStatement();
          break;
        case TK_FOREACH:
          ForEachStatement();
          break;
        case TK_SWITCH:
          SwitchStatement();
          break;
        case TK_LOCAL:
          LocalDeclStatement();
          break;
        case TK_RETURN:
        case TK_YIELD: {
          SQOpcode op;
          if( _token == TK_RETURN ) {
            op = _OP_RETURN;
          } else {
            op = _OP_YIELD;
            _fs->_bgenerator = true;
          }
          Lex();
          if( !IsEndOfStatement() ) {
            SQInteger retexp = _fs->GetCurrentPos() + 1;
            CommaExpr();
            if( op == _OP_RETURN && _fs->_traps > 0 )
            { _fs->AddInstruction( _OP_POPTRAP, _fs->_traps, 0 ); }
            _fs->_returnexp = retexp;
            _fs->AddInstruction( op, 1, _fs->PopTarget() );
          } else {
            if( op == _OP_RETURN && _fs->_traps > 0 )
            { _fs->AddInstruction( _OP_POPTRAP, _fs->_traps, 0 ); }
            _fs->_returnexp = -1;
            _fs->AddInstruction( op, 0xFF );
          }
          break;
        }
        case TK_BREAK:
          if( _fs->_breaktargets.size() <= 0 ) {
            Error( _SC( "'break' has to be in a loop block" ) );
          }
          if( _fs->_breaktargets.top() > 0 ) {
            _fs->AddInstruction( _OP_POPTRAP, _fs->_breaktargets.top(), 0 );
          }
          _fs->AddInstruction( _OP_JMP, 0, -1234 );
          _fs->_unresolvedbreaks.push_back( _fs->GetCurrentPos() );
          Lex();
          break;
        case TK_CONTINUE:
          if( _fs->_continuetargets.size() <= 0 ) {
            Error( _SC( "'continue' has to be in a loop block" ) );
          }
          if( _fs->_continuetargets.top() > 0 ) {
            _fs->AddInstruction( _OP_POPTRAP, _fs->_continuetargets.top(), 0 );
          }
          _fs->AddInstruction( _OP_JMP, 0, -1234 );
          _fs->_unresolvedcontinues.push_back( _fs->GetCurrentPos() );
          Lex();
          break;
        case TK_FUNCTION:
          FunctionStatement();
          break;
        case TK_CLASS:
          ClassStatement();
          break;
        case TK_ENUM:
          EnumStatement();
          break;
        case _SC( '{' ): {
          SQInteger stacksize = _fs->GetStackSize();
          Lex();
          Statements();
          Expect( _SC( '}' ) );
          _fs->SetStackSize( stacksize );
        }
        break;
        case TK_TRY:
          TryCatchStatement();
          break;
        case TK_THROW:
          Lex();
          CommaExpr();
          _fs->AddInstruction( _OP_THROW, _fs->PopTarget() );
          break;
        case TK_CONST: {
          Lex();
          SQObject id = Expect( TK_IDENTIFIER );
          Expect( '=' );
          SQObject val = ExpectScalar();
          OptionalSemicolon();
          SQTable *enums = _table( _ss( _vm )->_consts );
          SQObjectPtr strongid = id;
          enums->NewSlot( strongid, SQObjectPtr( val ) );
          strongid.Null();
        }
        break;
        default:
          CommaExpr();
          _fs->PopTarget();
          break;
      }
      _fs->SnoozeOpt();
    }
    void EmitDerefOp( SQOpcode op ) {
      SQInteger val = _fs->PopTarget();
      SQInteger key = _fs->PopTarget();
      SQInteger src = _fs->PopTarget();
      _fs->AddInstruction( op, _fs->PushTarget(), src, key, val );
    }
    void Emit2ArgsOP( SQOpcode op, SQInteger p3 = 0 ) {
      SQInteger p2 = _fs->PopTarget();
      SQInteger p1 = _fs->PopTarget();
      _fs->AddInstruction( op, _fs->PushTarget(), p1, p2, p3 );
    }
    void EmitCompoundArith( SQInteger tok, bool deref ) {
      SQInteger oper;
      switch( tok ) {
        case TK_MINUSEQ:
          oper = '-';
          break;
        case TK_PLUSEQ:
          oper = '+';
          break;
        case TK_MULEQ:
          oper = '*';
          break;
        case TK_DIVEQ:
          oper = '/';
          break;
        case TK_MODEQ:
          oper = '%';
          break;
        default:
          oper = 0;
          assert( 0 );
          break;
      };
      if( deref ) {
        SQInteger val = _fs->PopTarget();
        SQInteger key = _fs->PopTarget();
        SQInteger src = _fs->PopTarget();
        _fs->AddInstruction( _OP_COMPARITH, _fs->PushTarget(), ( src << 16 ) | val, key, oper );
      } else
      { Emit2ArgsOP( _OP_COMPARITHL, oper ); }
    }
    void CommaExpr() {
      for( Expression(); _token == ','; _fs->PopTarget(), Lex(), CommaExpr() );
    }
    ExpState Expression( bool funcarg = false ) {
      PushExpState();
      _exst._class_or_delete = false;
      _exst._funcarg = funcarg;
      LogicalOrExp();
      switch( _token )  {
        case _SC( '=' ):
        case TK_NEWSLOT:
        case TK_MINUSEQ:
        case TK_PLUSEQ:
        case TK_MULEQ:
        case TK_DIVEQ:
        case TK_MODEQ: {
          SQInteger op = _token;
          SQInteger ds = _exst._deref;
          bool freevar = _exst._freevar;
          if( ds == DEREF_NO_DEREF ) {
            Error( _SC( "can't assign expression" ) );
          }
          Lex();
          Expression();
          switch( op ) {
            case TK_NEWSLOT:
              if( freevar )
              { Error( _SC( "free variables cannot be modified" ) ); }
              if( ds == DEREF_FIELD )
              { EmitDerefOp( _OP_NEWSLOT ); }
              else
              { Error( _SC( "can't 'create' a local slot" ) ); }
              break;
            case _SC( '=' ):
              if( freevar )
              { Error( _SC( "free variables cannot be modified" ) ); }
              if( ds == DEREF_FIELD )
              { EmitDerefOp( _OP_SET ); }
              else {
                SQInteger p2 = _fs->PopTarget();
                SQInteger p1 = _fs->TopTarget();
                _fs->AddInstruction( _OP_MOVE, p1, p2 );
              }
              break;
            case TK_MINUSEQ:
            case TK_PLUSEQ:
            case TK_MULEQ:
            case TK_DIVEQ:
            case TK_MODEQ:
              EmitCompoundArith( op, ds == DEREF_FIELD );
              break;
          }
        }
        break;
        case _SC( '?' ): {
          Lex();
          _fs->AddInstruction( _OP_JZ, _fs->PopTarget() );
          SQInteger jzpos = _fs->GetCurrentPos();
          SQInteger trg = _fs->PushTarget();
          Expression();
          SQInteger first_exp = _fs->PopTarget();
          if( trg != first_exp ) {
            _fs->AddInstruction( _OP_MOVE, trg, first_exp );
          }
          SQInteger endfirstexp = _fs->GetCurrentPos();
          _fs->AddInstruction( _OP_JMP, 0, 0 );
          Expect( _SC( ':' ) );
          SQInteger jmppos = _fs->GetCurrentPos();
          Expression();
          SQInteger second_exp = _fs->PopTarget();
          if( trg != second_exp ) {
            _fs->AddInstruction( _OP_MOVE, trg, second_exp );
          }
          _fs->SetIntructionParam( jmppos, 1, _fs->GetCurrentPos() - jmppos );
          _fs->SetIntructionParam( jzpos, 1, endfirstexp - jzpos + 1 );
          _fs->SnoozeOpt();
        }
        break;
      }
      return PopExpState();
    }
    void BIN_EXP( SQOpcode op, void ( SQCompiler::*f )( void ), SQInteger op3 = 0 ) {
      Lex();
      ( this->*f )();
      SQInteger op1 = _fs->PopTarget();
      SQInteger op2 = _fs->PopTarget();
      _fs->AddInstruction( op, _fs->PushTarget(), op1, op2, op3 );
    }
    void LogicalOrExp() {
      LogicalAndExp();
      for( ;; )
        if( _token == TK_OR ) {
          SQInteger first_exp = _fs->PopTarget();
          SQInteger trg = _fs->PushTarget();
          _fs->AddInstruction( _OP_OR, trg, 0, first_exp, 0 );
          SQInteger jpos = _fs->GetCurrentPos();
          if( trg != first_exp ) {
            _fs->AddInstruction( _OP_MOVE, trg, first_exp );
          }
          Lex();
          LogicalOrExp();
          _fs->SnoozeOpt();
          SQInteger second_exp = _fs->PopTarget();
          if( trg != second_exp ) {
            _fs->AddInstruction( _OP_MOVE, trg, second_exp );
          }
          _fs->SnoozeOpt();
          _fs->SetIntructionParam( jpos, 1, ( _fs->GetCurrentPos() - jpos ) );
          break;
        } else
        { return; }
    }
    void LogicalAndExp() {
      BitwiseOrExp();
      for( ;; )
        switch( _token ) {
          case TK_AND: {
            SQInteger first_exp = _fs->PopTarget();
            SQInteger trg = _fs->PushTarget();
            _fs->AddInstruction( _OP_AND, trg, 0, first_exp, 0 );
            SQInteger jpos = _fs->GetCurrentPos();
            if( trg != first_exp )
            { _fs->AddInstruction( _OP_MOVE, trg, first_exp ); }
            Lex();
            LogicalAndExp();
            _fs->SnoozeOpt();
            SQInteger second_exp = _fs->PopTarget();
            if( trg != second_exp )
            { _fs->AddInstruction( _OP_MOVE, trg, second_exp ); }
            _fs->SnoozeOpt();
            _fs->SetIntructionParam( jpos, 1, ( _fs->GetCurrentPos() - jpos ) );
            break;
          }
          case TK_IN:
            BIN_EXP( _OP_EXISTS, &SQCompiler::BitwiseOrExp );
            break;
          case TK_INSTANCEOF:
            BIN_EXP( _OP_INSTANCEOF, &SQCompiler::BitwiseOrExp );
            break;
          default:
            return;
        }
    }
    void BitwiseOrExp() {
      BitwiseXorExp();
      for( ;; )
        if( _token == _SC( '|' ) ) {
          BIN_EXP( _OP_BITW, &SQCompiler::BitwiseXorExp, BW_OR );
        } else
        { return; }
    }
    void BitwiseXorExp() {
      BitwiseAndExp();
      for( ;; )
        if( _token == _SC( '^' ) ) {
          BIN_EXP( _OP_BITW, &SQCompiler::BitwiseAndExp, BW_XOR );
        } else
        { return; }
    }
    void BitwiseAndExp() {
      CompExp();
      for( ;; )
        if( _token == _SC( '&' ) ) {
          BIN_EXP( _OP_BITW, &SQCompiler::CompExp, BW_AND );
        } else
        { return; }
    }
    void CompExp() {
      ShiftExp();
      for( ;; )
        switch( _token ) {
          case TK_EQ:
            BIN_EXP( _OP_EQ, &SQCompiler::ShiftExp );
            break;
          case _SC( '>' ):
            BIN_EXP( _OP_CMP, &SQCompiler::ShiftExp, CMP_G );
            break;
          case _SC( '<' ):
            BIN_EXP( _OP_CMP, &SQCompiler::ShiftExp, CMP_L );
            break;
          case TK_GE:
            BIN_EXP( _OP_CMP, &SQCompiler::ShiftExp, CMP_GE );
            break;
          case TK_LE:
            BIN_EXP( _OP_CMP, &SQCompiler::ShiftExp, CMP_LE );
            break;
          case TK_NE:
            BIN_EXP( _OP_NE, &SQCompiler::ShiftExp );
            break;
          default:
            return;
        }
    }
    void ShiftExp() {
      PlusExp();
      for( ;; )
        switch( _token ) {
          case TK_USHIFTR:
            BIN_EXP( _OP_BITW, &SQCompiler::PlusExp, BW_USHIFTR );
            break;
          case TK_SHIFTL:
            BIN_EXP( _OP_BITW, &SQCompiler::PlusExp, BW_SHIFTL );
            break;
          case TK_SHIFTR:
            BIN_EXP( _OP_BITW, &SQCompiler::PlusExp, BW_SHIFTR );
            break;
          default:
            return;
        }
    }
    void PlusExp() {
      MultExp();
      for( ;; )
        switch( _token ) {
          case _SC( '+' ):
          case _SC( '-' ):
            BIN_EXP( _OP_ARITH, &SQCompiler::MultExp, _token );
            break;
          default:
            return;
        }
    }

    void MultExp() {
      PrefixedExpr();
      for( ;; )
        switch( _token ) {
          case _SC( '*' ):
          case _SC( '/' ):
          case _SC( '%' ):
            BIN_EXP( _OP_ARITH, &SQCompiler::PrefixedExpr, _token );
            break;
          default:
            return;
        }
    }

    void PrefixedExpr() {
      SQInteger pos = Factor();
      for( ;; ) {
        switch( _token ) {
          case _SC( '.' ): {
            pos = -1;
            Lex();
            if( _token == TK_PARENT ) {
              Lex();
              if( !NeedGet() )
              { Error( _SC( "parent cannot be set" ) ); }
              SQInteger src = _fs->PopTarget();
              _fs->AddInstruction( _OP_GETPARENT, _fs->PushTarget(), src );
            } else {
              _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( Expect( TK_IDENTIFIER ) ) );
              if( NeedGet() )
              { Emit2ArgsOP( _OP_GET ); }
            }
            _exst._deref = DEREF_FIELD;
            _exst._freevar = false;
          }
          break;
          case _SC( '[' ):
            if( _lex._prevtoken == _SC( '\n' ) )
            { Error( _SC( "cannot brake deref/or comma needed after [exp]=exp slot declaration" ) ); }
            Lex();
            Expression();
            Expect( _SC( ']' ) );
            pos = -1;
            if( NeedGet() )
            { Emit2ArgsOP( _OP_GET ); }
            _exst._deref = DEREF_FIELD;
            _exst._freevar = false;
            break;
          case TK_MINUSMINUS:
          case TK_PLUSPLUS:
            if( _exst._deref != DEREF_NO_DEREF && !IsEndOfStatement() ) {
              SQInteger tok = _token;
              Lex();
              if( pos < 0 )
              { Emit2ArgsOP( _OP_PINC, tok == TK_MINUSMINUS ? -1 : 1 ); }
              else {
                SQInteger src = _fs->PopTarget();
                _fs->AddInstruction( _OP_PINCL, _fs->PushTarget(), src, 0, tok == TK_MINUSMINUS ? -1 : 1 );
              }
            }
            return;
            break;
          case _SC( '(' ): {
            if( _exst._deref != DEREF_NO_DEREF ) {
              if( pos < 0 ) {
                SQInteger key = _fs->PopTarget();
                SQInteger table = _fs->PopTarget();
                SQInteger closure = _fs->PushTarget();
                SQInteger ttarget = _fs->PushTarget();
                _fs->AddInstruction( _OP_PREPCALL, closure, key, table, ttarget );
              } else
              { _fs->AddInstruction( _OP_MOVE, _fs->PushTarget(), 0 ); }
            } else
            { _fs->AddInstruction( _OP_MOVE, _fs->PushTarget(), 0 ); }
            _exst._deref = DEREF_NO_DEREF;
            Lex();
            FunctionCallArgs();
          }
          break;
          default:
            return;
        }
      }
    }
    SQInteger Factor() {
      _exst._deref = DEREF_NO_DEREF;
      switch( _token ) {
        case TK_STRING_LITERAL: {
          _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( _fs->CreateString( _lex._svalue, _lex._longstr.size() - 1 ) ) );
          Lex();
        }
        break;
        case TK_VARGC:
          Lex();
          _fs->AddInstruction( _OP_VARGC, _fs->PushTarget() );
          break;
        case TK_VARGV: {
          Lex();
          Expect( _SC( '[' ) );
          Expression();
          Expect( _SC( ']' ) );
          SQInteger src = _fs->PopTarget();
          _fs->AddInstruction( _OP_GETVARGV, _fs->PushTarget(), src );
        }
        break;
        case TK_IDENTIFIER:
        case TK_CONSTRUCTOR:
        case TK_THIS: {
          _exst._freevar = false;
          SQObject id;
          SQObject constant;
          switch( _token ) {
            case TK_IDENTIFIER:
              id = _fs->CreateString( _lex._svalue );
              break;
            case TK_THIS:
              id = _fs->CreateString( _SC( "this" ) );
              break;
            case TK_CONSTRUCTOR:
              id = _fs->CreateString( _SC( "constructor" ) );
              break;
          }
          SQInteger pos = -1;
          Lex();
          if( ( pos = _fs->GetLocalVariable( id ) ) == -1 ) {
            if( ( pos = _fs->GetOuterVariable( id ) ) != -1 ) {
              _exst._deref = _fs->PushTarget();
              _fs->AddInstruction( _OP_LOADFREEVAR, _exst._deref, pos );
              _exst._freevar = true;
            } else if( _fs->IsConstant( id, constant ) ) {
              SQObjectPtr constval;
              SQObject constid;
              if( type( constant ) == OT_TABLE ) {
                Expect( '.' );
                constid = Expect( TK_IDENTIFIER );
                if( !_table( constant )->Get( constid, constval ) ) {
                  constval.Null();
                  Error( _SC( "invalid constant [%s.%s]" ), _stringval( id ), _stringval( constid ) );
                }
              } else
              { constval = constant; }
              _exst._deref = _fs->PushTarget();
              SQObjectType ctype = type( constval );
              if( ctype == OT_INTEGER && ( _integer( constval ) & ( ~0x7FFFFFFF ) ) == 0 )
              { _fs->AddInstruction( _OP_LOADINT, _exst._deref, _integer( constval ) ); }
              else if( ctype == OT_FLOAT && sizeof( SQFloat ) == sizeof( SQInt32 ) ) {
                SQFloat f = _float( constval );
                _fs->AddInstruction( _OP_LOADFLOAT, _exst._deref, *( ( SQInt32 * )&f ) );
              } else
              { _fs->AddInstruction( _OP_LOAD, _exst._deref, _fs->GetConstant( constval ) ); }
              _exst._freevar = true;
            } else {
              _fs->PushTarget( 0 );
              _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( id ) );
              if( NeedGet() )
              { Emit2ArgsOP( _OP_GET ); }
              _exst._deref = DEREF_FIELD;
            }
          } else {
            _fs->PushTarget( pos );
            _exst._deref = pos;
          }
          return _exst._deref;
        }
        break;
        case TK_PARENT:
          Lex();
          _fs->AddInstruction( _OP_GETPARENT, _fs->PushTarget(), 0 );
          break;
        case TK_DOUBLE_COLON:
          _fs->AddInstruction( _OP_LOADROOTTABLE, _fs->PushTarget() );
          _exst._deref = DEREF_FIELD;
          _token = _SC( '.' );
          return -1;
          break;
        case TK_NULL:
          _fs->AddInstruction( _OP_LOADNULLS, _fs->PushTarget(), 1 );
          Lex();
          break;
        case TK_INTEGER: {
          if( ( _lex._nvalue & ( ~0x7FFFFFFF ) ) == 0 ) {
            _fs->AddInstruction( _OP_LOADINT, _fs->PushTarget(), _lex._nvalue );
          } else
          { _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant( _lex._nvalue ) ); }
          Lex();
        }
        break;
        case TK_FLOAT:
          if( sizeof( SQFloat ) == sizeof( SQInt32 ) ) {
            _fs->AddInstruction( _OP_LOADFLOAT, _fs->PushTarget(), *( ( SQInt32 * )&_lex._fvalue ) );
          } else
          { _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetNumericConstant( _lex._fvalue ) ); }
          Lex();
          break;
        case TK_TRUE:
        case TK_FALSE:
          _fs->AddInstruction( _OP_LOADBOOL, _fs->PushTarget(), _token == TK_TRUE ? 1 : 0 );
          Lex();
          break;
        case _SC( '[' ): {
          _fs->AddInstruction( _OP_NEWARRAY, _fs->PushTarget() );
          SQInteger apos = _fs->GetCurrentPos(), key = 0;
          Lex();
          while( _token != _SC( ']' ) ) {
            Expression();
            if( _token == _SC( ',' ) )
            { Lex(); }
            SQInteger val = _fs->PopTarget();
            SQInteger array = _fs->TopTarget();
            _fs->AddInstruction( _OP_APPENDARRAY, array, val );
            key++;
          }
          _fs->SetIntructionParam( apos, 1, key );
          Lex();
        }
        break;
        case _SC( '{' ): {
          _fs->AddInstruction( _OP_NEWTABLE, _fs->PushTarget() );
          Lex();
          ParseTableOrClass( _SC( ',' ) );
        }
        break;
        case TK_FUNCTION:
          FunctionExp( _token );
          break;
        case TK_CLASS:
          Lex();
          ClassExp();
          break;
        case _SC( '-' ):
          UnaryOP( _OP_NEG );
          break;
        case _SC( '!' ):
          UnaryOP( _OP_NOT );
          break;
        case _SC( '~' ):
          UnaryOP( _OP_BWNOT );
          break;
        case TK_TYPEOF :
          UnaryOP( _OP_TYPEOF );
          break;
        case TK_RESUME :
          UnaryOP( _OP_RESUME );
          break;
        case TK_CLONE :
          UnaryOP( _OP_CLONE );
          break;
        case TK_MINUSMINUS :
        case TK_PLUSPLUS :
          PrefixIncDec( _token );
          break;
        case TK_DELETE :
          DeleteExpr();
          break;
        case TK_DELEGATE :
          DelegateExpr();
          break;
        case _SC( '(' ):
          Lex();
          CommaExpr();
          Expect( _SC( ')' ) );
          break;
        default:
          Error( _SC( "expression expected" ) );
      }
      return -1;
    }
    void UnaryOP( SQOpcode op ) {
      Lex();
      PrefixedExpr();
      SQInteger src = _fs->PopTarget();
      _fs->AddInstruction( op, _fs->PushTarget(), src );
    }
    bool NeedGet() {
      switch( _token ) {
        case _SC( '=' ):
        case _SC( '(' ):
        case TK_NEWSLOT:
        case TK_PLUSPLUS:
        case TK_MINUSMINUS:
        case TK_PLUSEQ:
        case TK_MINUSEQ:
        case TK_MULEQ:
        case TK_DIVEQ:
        case TK_MODEQ:
          return false;
      }
      return ( !_exst._class_or_delete ) || ( _exst._class_or_delete && ( _token == _SC( '.' ) || _token == _SC( '[' ) ) );
    }

    void FunctionCallArgs() {
      SQInteger nargs = 1;
      while( _token != _SC( ')' ) ) {
        Expression( true );
        MoveIfCurrentTargetIsLocal();
        nargs++;
        if( _token == _SC( ',' ) ) {
          Lex();
          if( _token == ')' ) {
            Error( _SC( "expression expected, found ')'" ) );
          }
        }
      }
      Lex();
      for( SQInteger i = 0; i < ( nargs - 1 ); i++ ) {
        _fs->PopTarget();
      }
      SQInteger stackbase = _fs->PopTarget();
      SQInteger closure = _fs->PopTarget();
      _fs->AddInstruction( _OP_CALL, _fs->PushTarget(), closure, stackbase, nargs );
    }
    void ParseTableOrClass( SQInteger separator, SQInteger terminator = '}' ) {
      SQInteger tpos = _fs->GetCurrentPos(), nkeys = 0;
      while( _token != terminator ) {
        bool hasattrs = false;
        bool isstatic = false;
        if( separator == ';' ) {
          if( _token == TK_ATTR_OPEN ) {
            _fs->AddInstruction( _OP_NEWTABLE, _fs->PushTarget() );
            Lex();
            ParseTableOrClass( ',', TK_ATTR_CLOSE );
            hasattrs = true;
          }
          if( _token == TK_STATIC ) {
            isstatic = true;
            Lex();
          }
        }
        switch( _token ) {
          case TK_FUNCTION:
          case TK_CONSTRUCTOR: {
            SQInteger tk = _token;
            Lex();
            SQObject id = tk == TK_FUNCTION ? Expect( TK_IDENTIFIER ) : _fs->CreateString( _SC( "constructor" ) );
            Expect( _SC( '(' ) );
            _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( id ) );
            CreateFunction( id );
            _fs->AddInstruction( _OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0 );
          }
          break;
          case _SC( '[' ):
            Lex();
            CommaExpr();
            Expect( _SC( ']' ) );
            Expect( _SC( '=' ) );
            Expression();
            break;
          default :
            _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( Expect( TK_IDENTIFIER ) ) );
            Expect( _SC( '=' ) );
            Expression();
        }
        if( _token == separator ) {
          Lex();
        }
        nkeys++;
        SQInteger val = _fs->PopTarget();
        SQInteger key = _fs->PopTarget();
        SQInteger attrs = hasattrs ? _fs->PopTarget() : -1;
        assert( ( hasattrs && ( attrs == key - 1 ) ) || !hasattrs );
        int flags = ( hasattrs ? NEW_SLOT_ATTRIBUTES_FLAG : 0 ) | ( isstatic ? NEW_SLOT_STATIC_FLAG : 0 );
        SQInteger table = _fs->TopTarget();
        _fs->AddInstruction( _OP_NEWSLOTA, flags, table, key, val );
      }
      if( separator == _SC( ',' ) ) {
        _fs->SetIntructionParam( tpos, 1, nkeys );
      }
      Lex();
    }
    void LocalDeclStatement() {
      SQObject varname;
      do {
        Lex();
        varname = Expect( TK_IDENTIFIER );
        if( _token == _SC( '=' ) ) {
          Lex();
          Expression();
          SQInteger src = _fs->PopTarget();
          SQInteger dest = _fs->PushTarget();
          if( dest != src ) {
            _fs->AddInstruction( _OP_MOVE, dest, src );
          }
        } else
        { _fs->AddInstruction( _OP_LOADNULLS, _fs->PushTarget(), 1 ); }
        _fs->PopTarget();
        _fs->PushLocalVariable( varname );
      } while( _token == _SC( ',' ) );
    }
    void IfStatement() {
      SQInteger jmppos;
      bool haselse = false;
      Lex();
      Expect( _SC( '(' ) );
      CommaExpr();
      Expect( _SC( ')' ) );
      _fs->AddInstruction( _OP_JZ, _fs->PopTarget() );
      SQInteger jnepos = _fs->GetCurrentPos();
      SQInteger stacksize = _fs->GetStackSize();
      Statement();
      if( _token != _SC( '}' ) && _token != TK_ELSE ) {
        OptionalSemicolon();
      }
      CleanStack( stacksize );
      SQInteger endifblock = _fs->GetCurrentPos();
      if( _token == TK_ELSE ) {
        haselse = true;
        stacksize = _fs->GetStackSize();
        _fs->AddInstruction( _OP_JMP );
        jmppos = _fs->GetCurrentPos();
        Lex();
        Statement();
        OptionalSemicolon();
        CleanStack( stacksize );
        _fs->SetIntructionParam( jmppos, 1, _fs->GetCurrentPos() - jmppos );
      }
      _fs->SetIntructionParam( jnepos, 1, endifblock - jnepos + ( haselse ? 1 : 0 ) );
    }
    void WhileStatement() {
      SQInteger jzpos, jmppos;
      SQInteger stacksize = _fs->GetStackSize();
      jmppos = _fs->GetCurrentPos();
      Lex();
      Expect( _SC( '(' ) );
      CommaExpr();
      Expect( _SC( ')' ) );
      BEGIN_BREAKBLE_BLOCK();
      _fs->AddInstruction( _OP_JZ, _fs->PopTarget() );
      jzpos = _fs->GetCurrentPos();
      stacksize = _fs->GetStackSize();
      Statement();
      CleanStack( stacksize );
      _fs->AddInstruction( _OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1 );
      _fs->SetIntructionParam( jzpos, 1, _fs->GetCurrentPos() - jzpos );
      END_BREAKBLE_BLOCK( jmppos );
    }
    void DoWhileStatement() {
      Lex();
      SQInteger jzpos = _fs->GetCurrentPos();
      SQInteger stacksize = _fs->GetStackSize();
      BEGIN_BREAKBLE_BLOCK()
      Statement();
      CleanStack( stacksize );
      Expect( TK_WHILE );
      SQInteger continuetrg = _fs->GetCurrentPos();
      Expect( _SC( '(' ) );
      CommaExpr();
      Expect( _SC( ')' ) );
      _fs->AddInstruction( _OP_JNZ, _fs->PopTarget(), jzpos - _fs->GetCurrentPos() - 1 );
      END_BREAKBLE_BLOCK( continuetrg );
    }
    void ForStatement() {
      Lex();
      SQInteger stacksize = _fs->GetStackSize();
      Expect( _SC( '(' ) );
      if( _token == TK_LOCAL ) {
        LocalDeclStatement();
      } else if( _token != _SC( ';' ) ) {
        CommaExpr();
        _fs->PopTarget();
      }
      Expect( _SC( ';' ) );
      _fs->SnoozeOpt();
      SQInteger jmppos = _fs->GetCurrentPos();
      SQInteger jzpos = -1;
      if( _token != _SC( ';' ) ) {
        CommaExpr();
        _fs->AddInstruction( _OP_JZ, _fs->PopTarget() );
        jzpos = _fs->GetCurrentPos();
      }
      Expect( _SC( ';' ) );
      _fs->SnoozeOpt();
      SQInteger expstart = _fs->GetCurrentPos() + 1;
      if( _token != _SC( ')' ) ) {
        CommaExpr();
        _fs->PopTarget();
      }
      Expect( _SC( ')' ) );
      _fs->SnoozeOpt();
      SQInteger expend = _fs->GetCurrentPos();
      SQInteger expsize = ( expend - expstart ) + 1;
      SQInstructionVec exp;
      if( expsize > 0 ) {
        for( SQInteger i = 0; i < expsize; i++ ) {
          exp.push_back( _fs->GetInstruction( expstart + i ) );
        }
        _fs->PopInstructions( expsize );
      }
      BEGIN_BREAKBLE_BLOCK()
      Statement();
      SQInteger continuetrg = _fs->GetCurrentPos();
      if( expsize > 0 ) {
        for( SQInteger i = 0; i < expsize; i++ ) {
          _fs->AddInstruction( exp[i] );
        }
      }
      _fs->AddInstruction( _OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0 );
      if( jzpos >  0 ) {
        _fs->SetIntructionParam( jzpos, 1, _fs->GetCurrentPos() - jzpos );
      }
      CleanStack( stacksize );
      END_BREAKBLE_BLOCK( continuetrg );
    }
    void ForEachStatement() {
      SQObject idxname, valname;
      Lex();
      Expect( _SC( '(' ) );
      valname = Expect( TK_IDENTIFIER );
      if( _token == _SC( ',' ) ) {
        idxname = valname;
        Lex();
        valname = Expect( TK_IDENTIFIER );
      } else
      { idxname = _fs->CreateString( _SC( "@INDEX@" ) ); }
      Expect( TK_IN );
      SQInteger stacksize = _fs->GetStackSize();
      Expression();
      Expect( _SC( ')' ) );
      SQInteger container = _fs->TopTarget();
      SQInteger indexpos = _fs->PushLocalVariable( idxname );
      _fs->AddInstruction( _OP_LOADNULLS, indexpos, 1 );
      SQInteger valuepos = _fs->PushLocalVariable( valname );
      _fs->AddInstruction( _OP_LOADNULLS, valuepos, 1 );
      SQInteger itrpos = _fs->PushLocalVariable( _fs->CreateString( _SC( "@ITERATOR@" ) ) );
      _fs->AddInstruction( _OP_LOADNULLS, itrpos, 1 );
      SQInteger jmppos = _fs->GetCurrentPos();
      _fs->AddInstruction( _OP_FOREACH, container, 0, indexpos );
      SQInteger foreachpos = _fs->GetCurrentPos();
      _fs->AddInstruction( _OP_POSTFOREACH, container, 0, indexpos );
      BEGIN_BREAKBLE_BLOCK()
      Statement();
      _fs->AddInstruction( _OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1 );
      _fs->SetIntructionParam( foreachpos, 1, _fs->GetCurrentPos() - foreachpos );
      _fs->SetIntructionParam( foreachpos + 1, 1, _fs->GetCurrentPos() - foreachpos );
      CleanStack( stacksize );
      END_BREAKBLE_BLOCK( foreachpos - 1 );
    }
    void SwitchStatement() {
      Lex();
      Expect( _SC( '(' ) );
      CommaExpr();
      Expect( _SC( ')' ) );
      Expect( _SC( '{' ) );
      SQInteger expr = _fs->TopTarget();
      bool bfirst = true;
      SQInteger tonextcondjmp = -1;
      SQInteger skipcondjmp = -1;
      SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size();
      _fs->_breaktargets.push_back( 0 );
      while( _token == TK_CASE ) {
        if( !bfirst ) {
          _fs->AddInstruction( _OP_JMP, 0, 0 );
          skipcondjmp = _fs->GetCurrentPos();
          _fs->SetIntructionParam( tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp );
        }
        Lex();
        Expression();
        Expect( _SC( ':' ) );
        SQInteger trg = _fs->PopTarget();
        _fs->AddInstruction( _OP_EQ, trg, trg, expr );
        _fs->AddInstruction( _OP_JZ, trg, 0 );
        if( skipcondjmp != -1 ) {
          _fs->SetIntructionParam( skipcondjmp, 1, ( _fs->GetCurrentPos() - skipcondjmp ) );
        }
        tonextcondjmp = _fs->GetCurrentPos();
        SQInteger stacksize = _fs->GetStackSize();
        Statements();
        _fs->SetStackSize( stacksize );
        bfirst = false;
      }
      if( tonextcondjmp != -1 ) {
        _fs->SetIntructionParam( tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp );
      }
      if( _token == TK_DEFAULT ) {
        Lex();
        Expect( _SC( ':' ) );
        SQInteger stacksize = _fs->GetStackSize();
        Statements();
        _fs->SetStackSize( stacksize );
      }
      Expect( _SC( '}' ) );
      _fs->PopTarget();
      __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__;
      if( __nbreaks__ > 0 ) {
        ResolveBreaks( _fs, __nbreaks__ );
      }
      _fs->_breaktargets.pop_back();
    }
    void FunctionStatement() {
      SQObject id;
      Lex();
      id = Expect( TK_IDENTIFIER );
      _fs->PushTarget( 0 );
      _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( id ) );
      if( _token == TK_DOUBLE_COLON ) {
        Emit2ArgsOP( _OP_GET );
      }
      while( _token == TK_DOUBLE_COLON ) {
        Lex();
        id = Expect( TK_IDENTIFIER );
        _fs->AddInstruction( _OP_LOAD, _fs->PushTarget(), _fs->GetConstant( id ) );
        if( _token == TK_DOUBLE_COLON ) {
          Emit2ArgsOP( _OP_GET );
        }
      }
      Expect( _SC( '(' ) );
      CreateFunction( id );
      _fs->AddInstruction( _OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, 0 );
      EmitDerefOp( _OP_NEWSLOT );
      _fs->PopTarget();
    }
    void ClassStatement() {
      ExpState es;
      Lex();
      PushExpState();
      _exst._class_or_delete = true;
      _exst._funcarg = false;
      PrefixedExpr();
      es = PopExpState();
      if( es._deref == DEREF_NO_DEREF ) {
        Error( _SC( "invalid class name" ) );
      }
      if( es._deref == DEREF_FIELD ) {
        ClassExp();
        EmitDerefOp( _OP_NEWSLOT );
        _fs->PopTarget();
      } else
      { Error( _SC( "cannot create a class in a local with the syntax(class <local>)" ) ); }
    }
    SQObject ExpectScalar() {
      SQObject val;
      switch( _token ) {
        case TK_INTEGER:
          val._type = OT_INTEGER;
          val._unVal.nInteger = _lex._nvalue;
          break;
        case TK_FLOAT:
          val._type = OT_FLOAT;
          val._unVal.fFloat = _lex._fvalue;
          break;
        case TK_STRING_LITERAL:
          val = _fs->CreateString( _lex._svalue, _lex._longstr.size() - 1 );
          break;
        case '-':
          Lex();
          switch( _token ) {
            case TK_INTEGER:
              val._type = OT_INTEGER;
              val._unVal.nInteger = -_lex._nvalue;
              break;
            case TK_FLOAT:
              val._type = OT_FLOAT;
              val._unVal.fFloat = -_lex._fvalue;
              break;
            default:
              Error( _SC( "scalar expected : integer,float" ) );
          }
          break;
        default:
          Error( _SC( "scalar expected : integer,float or string" ) );
      }
      Lex();
      return val;
    }
    void EnumStatement() {
      Lex();
      SQObject id = Expect( TK_IDENTIFIER );
      Expect( _SC( '{' ) );
      SQObject table = _fs->CreateTable();
      SQInteger nval = 0;
      while( _token != _SC( '}' ) ) {
        SQObject key = Expect( TK_IDENTIFIER );
        SQObject val;
        if( _token == _SC( '=' ) ) {
          Lex();
          val = ExpectScalar();
        } else {
          val._type = OT_INTEGER;
          val._unVal.nInteger = nval++;
        }
        _table( table )->NewSlot( SQObjectPtr( key ), SQObjectPtr( val ) );
        if( _token == ',' ) {
          Lex();
        }
      }
      SQTable *enums = _table( _ss( _vm )->_consts );
      SQObjectPtr strongid = id;
      enums->NewSlot( SQObjectPtr( strongid ), SQObjectPtr( table ) );
      strongid.Null();
      Lex();
    }
    void TryCatchStatement() {
      SQObject exid;
      Lex();
      _fs->AddInstruction( _OP_PUSHTRAP, 0, 0 );
      _fs->_traps++;
      if( _fs->_breaktargets.size() ) {
        _fs->_breaktargets.top()++;
      }
      if( _fs->_continuetargets.size() ) {
        _fs->_continuetargets.top()++;
      }
      SQInteger trappos = _fs->GetCurrentPos();
      Statement();
      _fs->_traps--;
      _fs->AddInstruction( _OP_POPTRAP, 1, 0 );
      if( _fs->_breaktargets.size() ) {
        _fs->_breaktargets.top()--;
      }
      if( _fs->_continuetargets.size() ) {
        _fs->_continuetargets.top()--;
      }
      _fs->AddInstruction( _OP_JMP, 0, 0 );
      SQInteger jmppos = _fs->GetCurrentPos();
      _fs->SetIntructionParam( trappos, 1, ( _fs->GetCurrentPos() - trappos ) );
      Expect( TK_CATCH );
      Expect( _SC( '(' ) );
      exid = Expect( TK_IDENTIFIER );
      Expect( _SC( ')' ) );
      SQInteger stacksize = _fs->GetStackSize();
      SQInteger ex_target = _fs->PushLocalVariable( exid );
      _fs->SetIntructionParam( trappos, 0, ex_target );
      Statement();
      _fs->SetIntructionParams( jmppos, 0, ( _fs->GetCurrentPos() - jmppos ), 0 );
      CleanStack( stacksize );
    }
    void FunctionExp( SQInteger ftype ) {
      Lex();
      Expect( _SC( '(' ) );
      CreateFunction( _null_ );
      _fs->AddInstruction( _OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, ftype == TK_FUNCTION ? 0 : 1 );
    }
    void ClassExp() {
      SQInteger base = -1;
      SQInteger attrs = -1;
      if( _token == TK_EXTENDS ) {
        Lex();
        Expression();
        base = _fs->TopTarget();
      }
      if( _token == TK_ATTR_OPEN ) {
        Lex();
        _fs->AddInstruction( _OP_NEWTABLE, _fs->PushTarget() );
        ParseTableOrClass( _SC( ',' ), TK_ATTR_CLOSE );
        attrs = _fs->TopTarget();
      }
      Expect( _SC( '{' ) );
      if( attrs != -1 ) {
        _fs->PopTarget();
      }
      if( base != -1 ) {
        _fs->PopTarget();
      }
      _fs->AddInstruction( _OP_CLASS, _fs->PushTarget(), base, attrs );
      ParseTableOrClass( _SC( ';' ) );
    }
    void DelegateExpr() {
      Lex();
      CommaExpr();
      Expect( _SC( ':' ) );
      CommaExpr();
      SQInteger table = _fs->PopTarget(), delegate = _fs->PopTarget();
      _fs->AddInstruction( _OP_DELEGATE, _fs->PushTarget(), table, delegate );
    }
    void DeleteExpr() {
      ExpState es;
      Lex();
      PushExpState();
      _exst._class_or_delete = true;
      _exst._funcarg = false;
      PrefixedExpr();
      es = PopExpState();
      if( es._deref == DEREF_NO_DEREF ) {
        Error( _SC( "can't delete an expression" ) );
      }
      if( es._deref == DEREF_FIELD ) {
        Emit2ArgsOP( _OP_DELETE );
      } else
      { Error( _SC( "cannot delete a local" ) ); }
    }
    void PrefixIncDec( SQInteger token ) {
      ExpState es;
      Lex();
      PushExpState();
      _exst._class_or_delete = true;
      _exst._funcarg = false;
      PrefixedExpr();
      es = PopExpState();
      if( es._deref == DEREF_FIELD ) {
        Emit2ArgsOP( _OP_INC, token == TK_MINUSMINUS ? -1 : 1 );
      } else {
        SQInteger src = _fs->PopTarget();
        _fs->AddInstruction( _OP_INCL, _fs->PushTarget(), src, 0, token == TK_MINUSMINUS ? -1 : 1 );
      }
    }
    void CreateFunction( SQObject &name ) {
      SQFuncState *funcstate = _fs->PushChildState( _ss( _vm ) );
      funcstate->_name = name;
      SQObject paramname;
      funcstate->AddParameter( _fs->CreateString( _SC( "this" ) ) );
      funcstate->_sourcename = _sourcename;
      SQInteger defparams = 0;
      while( _token != _SC( ')' ) ) {
        if( _token == TK_VARPARAMS ) {
          if( defparams > 0 ) {
            Error( _SC( "function with default parameters cannot have variable number of parameters" ) );
          }
          funcstate->_varparams = true;
          Lex();
          if( _token != _SC( ')' ) ) {
            Error( _SC( "expected ')'" ) );
          }
          break;
        } else {
          paramname = Expect( TK_IDENTIFIER );
          funcstate->AddParameter( paramname );
          if( _token == _SC( '=' ) ) {
            Lex();
            Expression();
            funcstate->AddDefaultParam( _fs->TopTarget() );
            defparams++;
          } else {
            if( defparams > 0 )
            { Error( _SC( "expected '='" ) ); }
          }
          if( _token == _SC( ',' ) ) {
            Lex();
          } else if( _token != _SC( ')' ) ) {
            Error( _SC( "expected ')' or ','" ) );
          }
        }
      }
      Expect( _SC( ')' ) );
      for( SQInteger n = 0; n < defparams; n++ ) {
        _fs->PopTarget();
      }
      if( _token == _SC( ':' ) ) {
        Lex();
        Expect( _SC( '(' ) );
        while( _token != _SC( ')' ) ) {
          paramname = Expect( TK_IDENTIFIER );
          funcstate->AddOuterValue( paramname );
          if( _token == _SC( ',' ) ) {
            Lex();
          } else if( _token != _SC( ')' ) ) {
            Error( _SC( "expected ')' or ','" ) );
          }
        }
        Lex();
      }
      SQFuncState *currchunk = _fs;
      _fs = funcstate;
      Statement();
      funcstate->AddLineInfos( _lex._prevtoken == _SC( '\n' ) ? _lex._lasttokenline : _lex._currentline, _lineinfo, true );
      funcstate->AddInstruction( _OP_RETURN, -1 );
      funcstate->SetStackSize( 0 );
      SQFunctionProto *func = funcstate->BuildProto();
      #ifdef _DEBUG_DUMP
      funcstate->Dump( func );
      #endif
      _fs = currchunk;
      _fs->_functions.push_back( func );
      _fs->PopChildState();
    }
    void CleanStack( SQInteger stacksize ) {
      if( _fs->GetStackSize() != stacksize ) {
        _fs->SetStackSize( stacksize );
      }
    }
    void ResolveBreaks( SQFuncState *funcstate, SQInteger ntoresolve ) {
      while( ntoresolve > 0 ) {
        SQInteger pos = funcstate->_unresolvedbreaks.back();
        funcstate->_unresolvedbreaks.pop_back();
        funcstate->SetIntructionParams( pos, 0, funcstate->GetCurrentPos() - pos, 0 );
        ntoresolve--;
      }
    }
    void ResolveContinues( SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos ) {
      while( ntoresolve > 0 ) {
        SQInteger pos = funcstate->_unresolvedcontinues.back();
        funcstate->_unresolvedcontinues.pop_back();
        funcstate->SetIntructionParams( pos, 0, targetpos - pos, 0 );
        ntoresolve--;
      }
    }
  private:
    SQInteger _token;
    SQFuncState *_fs;
    SQObjectPtr _sourcename;
    SQLexer _lex;
    bool _lineinfo;
    bool _raiseerror;
    SQInteger _debugline;
    SQInteger _debugop;
    ExpStateVec _expstates;
    SQChar *compilererror;
    jmp_buf _errorjmp;
    SQVM *_vm;
};

bool Compile( SQVM *vm, SQLEXREADFUNC rg, SQUserPointer up, const SQChar *sourcename, SQObjectPtr &out, bool raiseerror, bool lineinfo ) {
  SQCompiler p( vm, rg, up, sourcename, raiseerror, lineinfo );
  return p.Compile( out );
}
